# Copyright (c) 2012 MaxMedia and Travis Warlick
# Licensed under the MIT License (see LICENSE)

require 'bundler'
require 'pathname'
require 'pp'

module Dionysus
  module TravisCI
    class GemfileGenerator
      def initialize(gemfile)
        @definition = load_definition(gemfile)
        @gemspec = load_gemspec(gemfile)
      end

      def generate(filename, options={})
        Pathname.new(filename).dirname.mkpath

        File.open(filename, "w") do |f|
          f.puts %(source "http://rubygems.org")

          @definition.dependencies.each do |dep|
            next if dep.name == @gemspec.name
            next if exclude_by_group?(dep, options[:without])
            f.puts dependency_line(dep)
          end

          case options[:add]
          when String
            f.puts options[:add]
          when Array
            options[:add].each {|ln| f.puts ln}
          else
            raise "Invalid :add option: %p"%[options[:add]] if options[:add]
          end

          f.puts %(gemspec :path => "../")
        end
      end

      private

      def dependency_line(dep)
        ln = [ %(gem "%s")%[dep.name] ]

        ln << %("%s")%[dep.requirement]

        groups = dep.groups.collect {|v| %(:#{v})}.join(", ")
        ln << %(:group => [%s])%[groups]

        if dep.autorequire == []
          ln << %(:require => false)
        elsif dep.autorequire
          requires = dep.autorequire.collect {|s| %("#{s}")}.join(",")
          ln << %(:require => [%s])%[requires]
        end

        if dep.source and dep.source.is_a?(Bundler::Source::Git)
          ln << %(:git => "%s")%[dep.source.options["git"]]
          %w[branch ref tag].each do |key|
            if dep.source.options[key]
              ln << %(%p => "%s")%[key.to_sym, dep.source.options[key]]
            end
          end
        elsif dep.source and dep.source.is_a?(Bundler::Source::Path)
          raise "Cannot create Gemfiles for Travis-CI with :path gems"
        elsif dep.source
          raise "Unknown source type: %s"%[dep.source.class]
        end

        ln.join(", ")
      end

      def exclude_by_group?(dep, *without)
        return false if without.nil? or without.empty?
        without = without.compact.flatten
        !(dep.groups & without).empty?
      end

      def load_definition(gemfile)
        Bundler::Dsl.new.tap do |dsl|
          dsl.instance_eval(Bundler.read_file(gemfile), "Gemfile", 1)
        end
      end

      def load_gemspec(gemfile)
        gemspecs = Dir[ Pathname(gemfile).dirname.join("*.gemspec") ]
        if gemspecs.empty?
          raise "Gemspec not found in same directory as %p"%[gemfile]
        elsif gemspecs.length > 1
          warn "Multiple gemspecs found, but only one is supported.  Using %p."%[gemspecs.first]
        end
        Bundler.load_gemspec(gemspecs.first)
      end
    end
  end
end
